package com.hisense.ovcloud.warehouse.service;

import cn.hutool.core.codec.Base64;
import com.hisense.ovcloud.commons.configs.base.BaseResponse;
import com.hisense.ovcloud.commons.configs.redis.RedisDao;
import com.hisense.ovcloud.commons.tools.AES256CbcUtil;
import com.hisense.ovcloud.commons.tools.AESEcbEncryptUtil;
import com.hisense.ovcloud.warehouse.Constants;
import com.hisense.ovcloud.warehouse.dto.req.CipherWithDKeyReq;
import com.hisense.ovcloud.warehouse.dto.req.FetchLatestCertInnerReq;
import com.hisense.ovcloud.warehouse.dto.resp.CipherWithDKeyResp;
import com.hisense.ovcloud.warehouse.dto.resp.GetCipheredDKeyResp;
import com.hisense.ovcloud.warehouse.tool.DkeyTool;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;

@Service
@Slf4j
public class WarehouseService {
    private final PemService pemService;

    private final RedisDao redisDao;

    private final DevicesService devicesService;

    public WarehouseService(RedisDao redisDao, DevicesService devicesService, PemService pemService) {
        this.redisDao = redisDao;
        this.devicesService = devicesService;
        this.pemService = pemService;
    }

    @SneakyThrows
    public Mono<CipherWithDKeyResp> cipherWithDKeyBase(Mono<CipherWithDKeyReq> req) {
        return req.flatMap(r -> redisDao.hget(Constants.DKEY_REDIS_PREFIX + r.getWifiId(), Constants.DKEY_REDIS_FIELD)
                .zipWhen(dkey -> {
                    if (StringUtils.isEmpty((String) dkey)) {
                        return Mono.just(CipherWithDKeyResp.builder().retCode(Constants.RESULT_FAILED).build());
                    } else {
                        // for aes256cbc
                        if (r.isCbcEnable()) {
                            return Mono.just(CipherWithDKeyResp.builder()
                                    .payload(AES256CbcUtil.encrypt256CBCWithoutIv((String) dkey, Base64.decode(r.getPayload().getBytes(StandardCharsets.UTF_8))))
                                    .retCode(Constants.RESULT_SUCCESS)
                                    .build());
                        } else {
                            return Mono.just(CipherWithDKeyResp.builder()
                                    .payload(AESEcbEncryptUtil.aesEcbEncryptToBase64((String) dkey, Base64.decode(r.getPayload().getBytes(StandardCharsets.UTF_8))))
                                    .retCode(Constants.RESULT_SUCCESS)
                                    .build());
                        }
                    }
                }, (x, y) -> y)
        );
    }

    public Mono<BaseResponse> cipherWithDKey(Mono<CipherWithDKeyReq> req) {
        return cipherWithDKeyBase(req).zipWhen(cipherWithDKeyResp -> {
            if (StringUtils.isEmpty(cipherWithDKeyResp.getPayload())) {
                return Mono.just(BaseResponse.builder().code(Constants.RESULT_FAILED).build());
            } else {
                return Mono.just(BaseResponse.builder().data(cipherWithDKeyResp).build());
            }
        }, (x, y) -> y);
    }

    private Mono<String> generateAndSaveDkey(String wifiId) {
        return Mono.just(DkeyTool.generateRandomKey(16))
                .flatMap(randomKey -> redisDao.hset(Constants.DKEY_REDIS_PREFIX + wifiId, Constants.DKEY_REDIS_FIELD, randomKey)
                        .zipWhen(res -> Mono.just(randomKey), (x, y) -> y));
    }

    public Mono<BaseResponse> getCipheredDKey(Mono<CipherWithDKeyReq> req) {
        return req.flatMap(r -> generateAndSaveDkey(r.getWifiId())
                .zipWhen(dkey -> {
                    log.debug("dkey: {} generated for wifiId {}, ", dkey, r.getWifiId());
                    // for aes256cbc
                    if (r.isCbcEnable()) {
                        return Mono.zip(
                                devicesService.getPublicKeyInner(Mono.just(r.getWifiId())).zipWhen(pubKey -> Mono.just(AES256CbcUtil.encrypt256CBCWithoutIv(pubKey, dkey.getBytes(StandardCharsets.UTF_8))), (x, y) -> y),
                                pemService.fetchLatestCertInner(Mono.just(FetchLatestCertInnerReq.builder().version(r.getVersion()).wifiId(r.getWifiId()).build()))

                        ).zipWhen(tup -> Mono.just(BaseResponse.builder().data(
                                GetCipheredDKeyResp.builder().dKey(tup.getT1()).fetchLatestCertInnerResp(tup.getT2()).build()
                        ).build()), (x, y) -> y);
                    } else {
                        return Mono.zip(
                                devicesService.getPublicKeyInner(Mono.just(r.getWifiId())).zipWhen(pubKey -> Mono.just(AESEcbEncryptUtil.aesEcbEncryptToBase64(pubKey, dkey.getBytes(StandardCharsets.UTF_8))), (x, y) -> y),
                                pemService.fetchLatestCertInner(Mono.just(FetchLatestCertInnerReq.builder().version(r.getVersion()).wifiId(r.getWifiId()).build()))

                        ).zipWhen(tup -> Mono.just(BaseResponse.builder().data(
                                GetCipheredDKeyResp.builder().dKey(tup.getT1()).fetchLatestCertInnerResp(tup.getT2()).build()
                        ).build()), (x, y) -> y);
                    }
                }, (x, y) -> y)
        );
    }

    public static String decrypt(String dkey, String encryptedText) throws Exception {
        byte[] keyBytes = dkey.getBytes(StandardCharsets.UTF_8);
        SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");

        // 解密
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);

        // 假设这是经过Base64编码的加密字符串
        String encryptedString = encryptedText; // 替换为实际的加密数据
        byte[] encryptedBytes =
                Base64.decode(encryptedString.getBytes(StandardCharsets.UTF_8));

        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
        String decryptedString = new String(decryptedBytes, StandardCharsets.UTF_8);

        return decryptedString;
    }

    public static void main(String[] args) throws Exception {
        System.out.println(decrypt("b87b1be28aad18cf758ea97587a0a299", "HKausqdCo+vzCa1VJ9lsS7TBtXISUUqz52PQFs0CHup6ooMhIO+G25TJHFwIA8YkhVygwNw0x8IsibNXDRt5HR+hD/Avg4TpHUA3AEEXzO5OcPfYXzWJvDe93HRNr9ifq70PSbweEjqm8j1aaHFw6A=="));
    }
}
